﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Windows.Media.Imaging;

namespace System.Windows.Media.Imaging
{
    /// <summary>
    /// WPF图像相关的功能扩展 for more:<see cref="https://github.com/reneschulte/WriteableBitmapEx"/>
    /// </summary>
    public static class ImageExtension
    {
        /// <summary>
        /// 将位图编码为指定的编码格式并返回其数据流
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="encoder">编码器，默认为png编码器</param>
        /// <returns></returns>
        public static Stream ToStream(this BitmapSource bitmap, BitmapEncoder encoder = null)
        {
            if (encoder == null)
            {
                encoder = new PngBitmapEncoder();
            }

            encoder.Frames.Add(BitmapFrame.Create(bitmap));
            var ms = new MemoryStream();
            encoder.Save(ms);
            ms.Seek(0, SeekOrigin.Begin);
            return ms;
        }
        /// <summary>
        /// 将位图编码为指定的编码格式并返回其字节数组
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="encoder">编码器，默认为png编码器</param>
        /// <returns></returns>
        public static byte[] ToByteArray(this BitmapSource bitmap, BitmapEncoder encoder = null)
        {
            using (var stream = bitmap.ToStream(encoder))
            {
                var bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                return bytes;
            }
        }
        /// <summary>
        /// 位块传输，将<see cref="Bitmap"/>中的图形数据写入<see cref="WriteableBitmap"/>。（此方法需要允许不安全代码）
        /// </summary>
        /// <param name="dst"></param>
        /// <param name="src"></param>
        /// <param name="pixelFormat">像素格式，会影响最终的图片质量，一般用Format32bppPArgb</param>
        public unsafe static void Blit(this WriteableBitmap dst, Bitmap src, System.Drawing.Imaging.PixelFormat pixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
        {
            int srcw = src.Width;
            int srch = src.Height;
            var rect = new Rectangle(0, 0, srcw, srch);
            int left = rect.Left < 0 ? 0 : rect.Left;
            int top = rect.Top < 0 ? 0 : rect.Top;
            int right = rect.Right > srcw ? srcw : rect.Right;
            int bottom = rect.Bottom > srch ? srch : rect.Bottom;
            if (left >= right || top >= bottom) return;
            BitmapData srcdata = src.LockBits(new Rectangle(0, 0, srcw, srch), ImageLockMode.ReadOnly, pixelFormat);
            dst.Lock();
            var srcints = (int*)srcdata.Scan0;
            var dstints = (int*)dst.BackBuffer.ToPointer();
            for (int j = top; j < bottom; ++j)
            {
                int y = j * srcw;
                for (int i = left; i < right; ++i)
                {
                    int index = y + i;
                    dstints[index] = srcints[index];
                }
            }
            dst.AddDirtyRect(new Int32Rect(left, top, right - left, bottom - top));
            src.UnlockBits(srcdata);
            dst.Unlock();
        }
    }
}
